<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2018/5/8
 * Time: 17:09
 */

namespace extend\payment;

class WxPay
{
    private $domain = 'https://api.mch.weixin.qq.com';
    private $apiKey = '';
    private $certPemPath = '';
    private $keyPemPath = '';


    // +----------------------------------------------------------------------
    // | 订单接口
    // +----------------------------------------------------------------------

    /**
     * 扫码下订单
     * @param $request
     * @return string
     * @throws \Exception
     */
    public function nativeOrder($request)
    {
        // 统一下单地址
        $request['trade_type'] = 'NATIVE';
        $res = $this->unifiedOrder($request);

        // 判断接口是否存在问题
        if ($res['return_code'] == 0) {
            return $res;
        }
        // 返回数据
        return $res;
    }

    /**
     * jsApi 下订单
     * @param $request
     * @return string
     * @throws \Exception
     */
    public function jsApiOrder($request)
    {
        // 统一下单地址
        $request['trade_type'] = 'JSAPI';
        $res = $this->unifiedOrder($request);

        // 判断接口是否存在问题
        if ($res['return_code'] == 0) {
            return $res;
        }
        // 时间戳
        $res['data']['timestamp'] = time();
        $res['data']['signType'] = 'MD5';
        $res['data']['package'] = "prepay_id={$res['data']['prepay_id']}";

        // 支付签名
        $paySign = "appId={$res['data']['appid']}&nonceStr={$res['data']['nonce_str']}&package={$res['data']['package']}&signType={$res['data']['signType']}&timeStamp={$res['data']['timestamp']}&key=" . $this->apiKey;
        // 处理支付签名
        $res['data']['paySign'] = strtoupper(md5($paySign));

        // 返回数据
        return $res;
    }

    /**
     * 统一下单接口
     * @param $request
     * @return false|string
     * @throws \Exception
     */
    public function unifiedOrder($request)
    {
        // 接口类型
        $apiType = 'order';
        // 接口地址
        $urlApi = '/pay/unifiedorder';

        // 请求参数处理
        $paramSign = $this->beforeRequest($request, $apiType);

        // 商品信息
        $paramSign['body'] = $request['desc'];
        // 订单号
        $paramSign['out_trade_no'] = $request['order_num'];
        // 金额
        $paramSign['total_fee'] = $request['order_money'];
        // ip 地址
        $paramSign['spbill_create_ip'] = $request['ip_address'];
        // 交易类型
        $paramSign['trade_type'] = $request['trade_type'];
        // 通知地址
        $paramSign['notify_url'] = $request['notify_url'];
        // openid
        $paramSign['openid'] = $request['openid'];

        // 商品ID (NATIVE : 扫码模式必填)
        $paramSign['product_id'] = $request['product_id'];

        // 接口地址
        $response = $this->sendRequest($urlApi, $paramSign);
        // 返回结果
        return $this->afterRequest($response);
    }


    /**
     * 刷卡支付
     * @param $request
     * @return string
     * @throws \Exception
     */
    public function microOrder($request)
    {
        // 接口类型
        $apiType = 'order';
        // 接口地址
        $urlApi = '/pay/micropay';

        // 请求参数处理
        $paramSign = $this->beforeRequest($request, $apiType);

        // 商品信息
        $paramSign['body'] = $request['desc'];
        // 订单号
        $paramSign['out_trade_no'] = $request['order_num'];
        // 金额
        $paramSign['total_fee'] = $request['order_money'];
        // ip 地址
        $paramSign['spbill_create_ip'] = $request['ip_address'];
        // 授权码
        $paramSign['auth_code'] = $request['auth_code'];

        // 接口地址
        $response = $this->sendRequest($urlApi, $paramSign);
        // 返回结果
        return $this->afterRequest($response);
    }


    /**
     * 订单查询接口
     * @param $request
     * order_num_type 有两个值：
     *          transaction_id(微信订单号) 和 out_trade_no(商户订单号)
     * @return string
     * @throws \Exception
     */
    public function orderQuery($request)
    {
        // 接口类型
        $apiType = 'order';
        // 接口地址
        $urlApi = '/pay/orderquery';

        // 请求参数处理
        $paramSign = $this->beforeRequest($request, $apiType);
        // 查询订单类型，和相对应的订单号
        $paramSign[$request['order_num_type']] = $request['order_num'];

        // 接口地址
        $response = $this->sendRequest($urlApi, $paramSign);
        // 返回结果
        return $this->afterRequest($response);

    }


    /**
     * 退款接口
     * @param $request
     * order_num_type 有两个值：
     *          transaction_id(微信订单号) 和 out_trade_no(商户订单号)
     * @return string
     * @throws \Exception
     */
    public function refund($request)
    {
        // 接口类型
        $apiType = 'order';
        // 接口地址
        $urlApi = '/secapi/pay/refund';

        // 请求参数处理
        $paramSign = $this->beforeRequest($request, $apiType);

        // 查询订单类型，和相对应的订单号
        $paramSign[$request['order_num_type']] = $request['order_num'];
        // 商户退款单号
        $paramSign['out_refund_no'] = $request['refund_num'];
        // 订单金额
        $paramSign['total_fee'] = $request['order_money'];
        // 退款金额
        $paramSign['refund_fee'] = $request['refund_money'];
        // 退款原因
        $paramSign['refund_desc'] = $request['refund_reason'];
        // 退款通知地址 与 支付通知地址区分开来
        $paramSign['notify_url'] = $request['notify_url'];

        // 接口地址
        $response = $this->sendRequest($urlApi, $paramSign, true);
        // 返回结果
        return $this->afterRequest($response);
    }


    /**
     * 退款订单查询
     * @param $request
     * order_num_type 有四个值：
     *          transaction_id(微信订单号) 和 out_trade_no(商户订单号)
     *          out_refund_no(商户退款单号) 和 refund_id(微信退款单号)
     * @return string
     * @throws \Exception
     */
    public function refundQuery($request)
    {
        // 接口类型
        $apiType = 'order';
        // 接口地址
        $urlApi = '/pay/refundquery';

        // 请求参数处理
        $paramSign = $this->beforeRequest($request, $apiType);
        // 查询订单类型，和相对应的订单号
        $paramSign[$request['order_num_type']] = $request['order_num'];

        $response = $this->sendRequest($urlApi, $paramSign);
        return $this->afterRequest($response);
    }


    /**
     * 关闭订单接口
     * @param $request
     * @return string
     * @throws \Exception
     */
    public function closeOrder($request)
    {
        // 接口类型
        $apiType = 'order';
        // 接口地址
        $urlApi = '/pay/closeorder';
        // 请求参数处理
        $paramSign = $this->beforeRequest($request, $apiType);
        // 查询订单类型，和相对应的订单号
        $paramSign['out_trade_no'] = $request['order_num'];

        $response = $this->sendRequest($urlApi, $paramSign);
        return $this->afterRequest($response);
    }


    // +----------------------------------------------------------------------
    // | 企业接口
    // +----------------------------------------------------------------------
    /**
     * 企业支付到零钱接口
     * @param $request
     * @return string
     * @throws \Exception
     */
    public function transfers($request)
    {
        // 接口类型
        $apiType = 'transfers';
        // 接口地址
        $urlApi = '/mmpaymkttransfers/promotion/transfers';

        // 请求参数处理
        $paramSign = $this->beforeRequest($request, $apiType);
        // 商户订单号
        $paramSign["partner_trade_no"] = $request["order_num"];
        // 用户openid
        $paramSign["openid"] = $request["openid"];
        // 校验用户姓名选项
        $paramSign["check_name"] = !empty($request["check_name"]) ? $request["check_name"] : "FORCE_CHECK";
        // 收款用户姓名
        if (!empty($request["bank_user_name"])) {
            $paramSign["re_user_name"] = $request["bank_user_name"];
        }
        // 金额
        $paramSign["amount"] = $request["order_money"];
        // 企业付款描述信息
        $paramSign["desc"] = $request["remark"];
        // ip 地址
        $paramSign["spbill_create_ip"] = $request["ip_address"];

        $response = $this->sendRequest($urlApi, $paramSign, true);
        return $this->afterRequest($response);
    }

    /**
     * 查询企业支付到零钱接口
     * @param $request
     * @return string
     * @throws \Exception
     */
    public function gettransferinfo($request)
    {
        // 接口类型
        $apiType = 'query';
        // 接口地址
        $urlApi = '/mmpaymkttransfers/gettransferinfo';

        // 请求参数处理
        $paramSign = $this->beforeRequest($request, $apiType);

        // 商户企业付款单号
        $paramSign["partner_trade_no"] = $request["order_num"];

        $response = $this->sendRequest($urlApi, $paramSign, true);
        return $this->afterRequest($response);
    }


    // +----------------------------------------------------------------------
    // | 红包接口
    // +----------------------------------------------------------------------
    /**
     * 普通红包
     * @param $request
     * @return string
     * @throws \Exception
     */
    public function sendredpack($request)
    {
        // 接口类型
        $apiType = 'query';
        // 接口地址
        $urlApi = '/mmpaymkttransfers/sendredpack';

        // 请求参数处理
        $paramSign = $this->beforeRequest($request, $apiType);

        // 订单号
        $paramSign["mch_billno"] = $request["order_num"];
        // 发送的openid
        $paramSign["re_openid"] = $request["openid"];
        // 金额
        $paramSign["total_amount"] = $request["order_money"];
        // 发放人数
        $paramSign["total_num"] = 1;
        // 发放ip地址
        $paramSign["client_ip"] = $request["ip_address"];
        // 祝福语
        $paramSign["wishing"] = $request["wishing"];
        // 活动名称
        $paramSign["act_name"] = $request["activity_name"];
        // 备注
        $paramSign["remark"] = $request["remark"];

        $response = $this->sendRequest($urlApi, $paramSign, true);
        return $this->afterRequest($response);
    }

    /**
     * 发送裂变红包
     * @param $request
     * @return string
     * @throws \Exception
     */
    public function sendgroupredpack($request)
    {
        // 接口类型
        $apiType = 'red_append';
        // 接口地址
        $urlApi = '/mmpaymkttransfers/sendredpack';

        // 请求参数处理
        $paramSign = $this->beforeRequest($request, $apiType);

        // 订单号
        $paramSign["mch_billno"] = $request["order_num"];
        // 发送的openid
        $paramSign["re_openid"] = $request["openid"];
        // 金额
        $paramSign["total_amount"] = $request["order_money"];
        // 发放人数
        $paramSign["total_num"] = $request["order_people_num"] ?? 1;
        // 发放ip地址
        $paramSign["client_ip"] = $request["ip_address"];
        // 祝福语
        $paramSign["wishing"] = $request["wishing"];
        // 红包金额设置方式：ALL_RAND（由微信进行随机分配）
        $paramSign["amt_type"] = $request["amt_type"] ?? "ALL_RAND";
        // 活动名称
        $paramSign["act_name"] = $request["activity_name"];
        // 备注
        $paramSign["remark"] = $request["remark"];

        $response = $this->sendRequest($urlApi, $paramSign, true);
        return $this->afterRequest($response);
    }

    /**
     * 红包查询记录
     * @param $request
     * @return string
     * @throws \Exception
     */
    public function gethbinfo($request)
    {
        // 接口类型
        $apiType = 'query';
        // 接口地址
        $urlApi = '/mmpaymkttransfers/gethbinfo';

        // 请求参数处理
        $paramSign = $this->beforeRequest($request, $apiType);

        // 订单号
        $paramSign["mch_billno"] = $request["order_num"];
        // 查询类型
        $paramSign["bill_type"] = "MCHT";

        $response = $this->sendRequest($urlApi, $paramSign, true);
        return $this->afterRequest($response);
    }


    // +----------------------------------------------------------------------
    // | 数据处理
    // +----------------------------------------------------------------------
    /**
     * 请求数据处理
     * @param array $request
     * @param string $request_type
     * @return mixed
     */
    public function beforeRequest($request = [], $request_type = 'order')
    {
        $this->apiKey = $request['api_key'];
        $this->certPemPath = $request['cert_pem'];
        $this->keyPemPath = $request['key_pem'];

        switch ($request_type) {
            // 填充订单数据
            case 'order' :
                $paramSign['mch_id'] = $request['mchid'];
                $paramSign['appid'] = $request['appid'];
                $paramSign['sign_type'] = 'MD5';
                break;
            // 填充红包查询数据
            case 'query' :
                $paramSign['mch_id'] = $request['mchid'];
                $paramSign['appid'] = $request['appid'];
                break;
            // 填充红包数据
            case 'red_append' :
                $paramSign['mch_id'] = $request['mchid'];
                $paramSign['wxappid'] = $request['appid'];
                $paramSign['send_name'] = $request['mch_name'];
                break;
            // 填充企业支付到零钱数据
            case 'transfers' :
                $paramSign['mchid'] = $request['mchid'];
                $paramSign['mch_appid'] = $request['appid'];
                break;
            // 填充企业支付到银行数据
            case 'sptrans' :
                $paramSign['mch_id'] = $request['mchid'];
                break;
            default:
                $paramSign = [];
        }

        return $paramSign;
    }

    /**
     * @param $url
     * @param $paramSign
     * @param bool $is_ssh
     * @return array
     * @throws \Exception
     */
    public function sendRequest($url, $paramSign, $is_ssh = false)
    {
        libxml_disable_entity_loader(true);

        $url = $this->domain . $url;
        $paramSign['nonce_str'] = nonceStr();
        $paramSign['sign'] = wxSignature($paramSign, $this->apiKey);


        $ssh_config = [
            "certPemPath" => $this->certPemPath,
            "keyPemPath" => $this->keyPemPath,
        ];

        // 将数据转化为 XML 格式
        $data = array2xml($paramSign);
        // 发送请求
        $resXml = httpRequest($url, 'POST', $data, [], $is_ssh, $ssh_config, false);

        // 将XML 转化为 数组
        $response = xml2array($resXml);

        return $response;
    }


    public function afterRequest($response)
    {
        // 判断接口返回结果
        if (!empty($response['return_code']) && $response['return_code'] == 'SUCCESS') {
            // 判断提交是否成功
            if (!empty($response['result_code']) && $response['result_code'] == 'FAIL') {
                // 接口返回失败
                $res['return_code'] = 0;
                $res['return_msg'] = $response['err_code_des'];
            } else {
                // 接口返回成功
                $res['data'] = $response;
                $res['return_code'] = 1;
                $res['return_msg'] = 'SUCCESS';
            }
        } else {
            // 接口返回失败
            $res['return_code'] = 0;
            $res['return_msg'] = $response['return_msg'];
        }
        // 返回 json 数据
        return $res;
    }

}